using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Reactive.Linq;
using ReactiveChannelMessaging.Core;
using ReactiveChannelMessaging.Communication;
using ReactiveChannelMessaging.Services;
using System.IO.Ports;

namespace ReactiveChannelMessaging.Examples;

/// <summary>
/// 通讯演示程序 - 展示串口、TCP、UDP的实际应用
/// </summary>
class CommunicationDemo
{
    public static async Task RunCommunicationDemo(string[] args)
    {
        Console.WriteLine("🌐 Real-World Communication Demo Starting...");
        Console.WriteLine("Supporting Serial, TCP, and UDP communications");

        // 配置服务
        var host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((context, services) =>
            {
                services.AddSingleton<ReactiveMessageBus>();
                services.AddHostedService<MessageOrchestrator>();
                services.AddSingleton<CommunicationManager>();
                services.AddLogging(builder =>
                {
                    builder.AddConsole();
                    builder.SetMinimumLevel(LogLevel.Information);
                });
            })
            .Build();

        // 启动服务
        var hostTask = host.RunAsync();

        // 获取通讯管理器
        var commManager = host.Services.GetRequiredService<CommunicationManager>();
        await commManager.InitializeAsync();

        // 设置监控
        SetupCommunicationMonitoring(commManager);

        Console.WriteLine("\n📡 Available Commands:");
        PrintCommands();

        // 运行交互式命令循环
        await RunCommandLoop(commManager);

        // 优雅关闭
        await host.StopAsync();
        await hostTask;
        await commManager.DisposeAsync();
    }

    private static void SetupCommunicationMonitoring(CommunicationManager manager)
    {
        // 监控串口数据
        manager.SerialDataReceived
            .Subscribe(data =>
            {
                var hex = Convert.ToHexString(data.Data);
                var ascii = GetPrintableAscii(data.Data);
                Console.WriteLine($"📟 [SERIAL:{data.PortName}] RX: {hex} | {ascii}");
            });

        // 监控TCP数据
        manager.TcpDataReceived
            .Subscribe(data =>
            {
                var text = System.Text.Encoding.UTF8.GetString(data.Data);
                Console.WriteLine($"🔗 [TCP:{data.ClientId}] {data.Direction}: {text}");
            });

        // 监控UDP数据
        manager.UdpDataReceived
            .Subscribe(data =>
            {
                var text = System.Text.Encoding.UTF8.GetString(data.Data);
                Console.WriteLine($"📡 [UDP:{data.RemoteEndPoint}] {data.Direction}: {text}");
            });

        // 监控连接状态
        manager.ConnectionStatusChanged
            .Subscribe(status =>
            {
                var emoji = status.Contains("Connected") || status.Contains("Listening") ? "✅" : 
                           status.Contains("Error") ? "❌" : "ℹ️";
                Console.WriteLine($"{emoji} [STATUS] {status}");
            });
    }

    private static async Task RunCommandLoop(CommunicationManager manager)
    {
        while (true)
        {
            Console.Write("\n> ");
            var input = Console.ReadLine()?.Trim().ToLowerInvariant();
            
            if (string.IsNullOrEmpty(input)) continue;

            try
            {
                switch (input.Split(' ')[0])
                {
                    case "quit" or "q":
                        return;

                    case "help" or "h":
                        PrintCommands();
                        break;

                    case "serial":
                        await HandleSerialCommand(manager, input);
                        break;

                    case "tcp":
                        await HandleTcpCommand(manager, input);
                        break;

                    case "udp":
                        await HandleUdpCommand(manager, input);
                        break;

                    case "list":
                        await HandleListCommand(manager, input);
                        break;

                    case "status":
                        await HandleStatusCommand(manager);
                        break;

                    case "demo":
                        await HandleDemoCommand(manager, input);
                        break;

                    default:
                        Console.WriteLine("❌ Unknown command. Type 'help' for available commands.");
                        break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ Error: {ex.Message}");
            }
        }
    }

    private static void PrintCommands()
    {
        Console.WriteLine("📖 Available Commands:");
        Console.WriteLine("  Serial Commands:");
        Console.WriteLine("    serial list                    - List available serial ports");
        Console.WriteLine("    serial open <port> <baud>      - Open serial port (e.g., COM3 9600)");
        Console.WriteLine("    serial send <port> <text>      - Send text to serial port");
        Console.WriteLine("    serial hex <port> <hex>        - Send hex data (e.g., FF 00 AA)");
        Console.WriteLine("    serial close <port>            - Close serial port");
        Console.WriteLine();
        Console.WriteLine("  TCP Commands:");
        Console.WriteLine("    tcp server <port>              - Start TCP server");
        Console.WriteLine("    tcp client <host> <port>       - Connect TCP client");
        Console.WriteLine("    tcp send <text>                - Send text via TCP");
        Console.WriteLine("    tcp broadcast <text>           - Broadcast to all TCP clients");
        Console.WriteLine("    tcp stop                       - Stop TCP communication");
        Console.WriteLine();
        Console.WriteLine("  UDP Commands:");
        Console.WriteLine("    udp start <port>               - Start UDP listener");
        Console.WriteLine("    udp send <host> <port> <text>  - Send UDP message");
        Console.WriteLine("    udp broadcast <port> <text>    - Broadcast UDP message");
        Console.WriteLine("    udp stop                       - Stop UDP communication");
        Console.WriteLine();
        Console.WriteLine("  General Commands:");
        Console.WriteLine("    list connections               - List all active connections");
        Console.WriteLine("    status                         - Show system status");
        Console.WriteLine("    demo <type>                    - Run demo (serial/tcp/udp/all)");
        Console.WriteLine("    help/h                         - Show this help");
        Console.WriteLine("    quit/q                         - Exit program");
    }

    private static async Task HandleSerialCommand(CommunicationManager manager, string input)
    {
        var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length < 2) return;

        switch (parts[1])
        {
            case "list":
                var ports = ReactiveSerialCommunication.GetAvailablePorts();
                Console.WriteLine($"📟 Available Serial Ports: {string.Join(", ", ports)}");
                break;

            case "open" when parts.Length >= 4:
                var success = await manager.OpenSerialPortAsync(parts[2], int.Parse(parts[3]));
                Console.WriteLine(success ? $"✅ Serial port {parts[2]} opened" : $"❌ Failed to open {parts[2]}");
                break;

            case "send" when parts.Length >= 4:
                var text = string.Join(" ", parts.Skip(3));
                await manager.SendSerialStringAsync(parts[2], text);
                Console.WriteLine($"📤 Sent to {parts[2]}: {text}");
                break;

            case "hex" when parts.Length >= 4:
                var hex = string.Join(" ", parts.Skip(3));
                await manager.SendSerialHexAsync(parts[2], hex);
                Console.WriteLine($"📤 Sent hex to {parts[2]}: {hex}");
                break;

            case "close" when parts.Length >= 3:
                await manager.CloseSerialPortAsync(parts[2]);
                Console.WriteLine($"🔒 Serial port {parts[2]} closed");
                break;

            default:
                Console.WriteLine("❌ Invalid serial command");
                break;
        }
    }

    private static async Task HandleTcpCommand(CommunicationManager manager, string input)
    {
        var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length < 2) return;

        switch (parts[1])
        {
            case "server" when parts.Length >= 3:
                var success = await manager.StartTcpServerAsync(int.Parse(parts[2]));
                Console.WriteLine(success ? $"✅ TCP server started on port {parts[2]}" : "❌ Failed to start TCP server");
                break;

            case "client" when parts.Length >= 4:
                success = await manager.ConnectTcpClientAsync(parts[2], int.Parse(parts[3]));
                Console.WriteLine(success ? $"✅ Connected to {parts[2]}:{parts[3]}" : "❌ Failed to connect");
                break;

            case "send" when parts.Length >= 3:
                var text = string.Join(" ", parts.Skip(2));
                await manager.SendTcpStringAsync(text);
                Console.WriteLine($"📤 TCP sent: {text}");
                break;

            case "broadcast" when parts.Length >= 3:
                text = string.Join(" ", parts.Skip(2));
                var count = await manager.BroadcastTcpStringAsync(text);
                Console.WriteLine($"📡 Broadcasted to {count} clients: {text}");
                break;

            case "stop":
                await manager.StopTcpAsync();
                Console.WriteLine("🛑 TCP communication stopped");
                break;

            default:
                Console.WriteLine("❌ Invalid TCP command");
                break;
        }
    }

    private static async Task HandleUdpCommand(CommunicationManager manager, string input)
    {
        var parts = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length < 2) return;

        switch (parts[1])
        {
            case "start" when parts.Length >= 3:
                var success = await manager.StartUdpAsync(int.Parse(parts[2]));
                Console.WriteLine(success ? $"✅ UDP started on port {parts[2]}" : "❌ Failed to start UDP");
                break;

            case "send" when parts.Length >= 5:
                var text = string.Join(" ", parts.Skip(4));
                success = await manager.SendUdpStringAsync(parts[2], int.Parse(parts[3]), text);
                Console.WriteLine(success ? $"📤 UDP sent to {parts[2]}:{parts[3]}: {text}" : "❌ Failed to send");
                break;

            case "broadcast" when parts.Length >= 4:
                text = string.Join(" ", parts.Skip(3));
                success = await manager.BroadcastUdpStringAsync(int.Parse(parts[2]), text);
                Console.WriteLine(success ? $"📡 UDP broadcasted on port {parts[2]}: {text}" : "❌ Failed to broadcast");
                break;

            case "stop":
                await manager.StopUdpAsync();
                Console.WriteLine("🛑 UDP communication stopped");
                break;

            default:
                Console.WriteLine("❌ Invalid UDP command");
                break;
        }
    }

    private static async Task HandleListCommand(CommunicationManager manager, string input)
    {
        Console.WriteLine("📋 Active Connections:");
        await manager.PrintConnectionStatusAsync();
    }

    private static async Task HandleStatusCommand(CommunicationManager manager)
    {
        Console.WriteLine("📊 System Status:");
        await manager.PrintSystemStatusAsync();
    }

    private static async Task HandleDemoCommand(CommunicationManager manager, string input)
    {
        var parts = input.Split(' ');
        var demoType = parts.Length > 1 ? parts[1] : "all";

        Console.WriteLine($"🎬 Running {demoType.ToUpper()} demo...");

        switch (demoType)
        {
            case "serial":
                await RunSerialDemo(manager);
                break;
            case "tcp":
                await RunTcpDemo(manager);
                break;
            case "udp":
                await RunUdpDemo(manager);
                break;
            case "all":
                await RunAllDemo(manager);
                break;
            default:
                Console.WriteLine("❌ Unknown demo type. Use: serial, tcp, udp, or all");
                break;
        }
    }

    private static async Task RunSerialDemo(CommunicationManager manager)
    {
        var ports = ReactiveSerialCommunication.GetAvailablePorts();
        if (ports.Length == 0)
        {
            Console.WriteLine("⚠️ No serial ports available for demo");
            return;
        }

        var port = ports[0];
        Console.WriteLine($"📟 Serial Demo using port {port}");
        
        if (await manager.OpenSerialPortAsync(port, 9600))
        {
            await Task.Delay(1000);
            await manager.SendSerialStringAsync(port, "Hello Serial World!");
            await manager.SendSerialHexAsync(port, "FF 00 AA BB CC");
            await Task.Delay(2000);
            await manager.CloseSerialPortAsync(port);
        }
    }

    private static async Task RunTcpDemo(CommunicationManager manager)
    {
        Console.WriteLine("🔗 TCP Demo - Starting server and client");
        
        // 启动服务器
        if (await manager.StartTcpServerAsync(12345))
        {
            await Task.Delay(1000);
            
            // 连接客户端
            if (await manager.ConnectTcpClientAsync("127.0.0.1", 12345))
            {
                await Task.Delay(1000);
                await manager.SendTcpStringAsync("Hello TCP World!");
                await manager.BroadcastTcpStringAsync("Broadcast message to all clients");
                await Task.Delay(2000);
            }
            
            await manager.StopTcpAsync();
        }
    }

    private static async Task RunUdpDemo(CommunicationManager manager)
    {
        Console.WriteLine("📡 UDP Demo - Point-to-point and broadcast");
        
        if (await manager.StartUdpAsync(54321))
        {
            await Task.Delay(1000);
            await manager.SendUdpStringAsync("127.0.0.1", 54321, "Hello UDP World!");
            await manager.BroadcastUdpStringAsync(54321, "UDP Broadcast message");
            await Task.Delay(2000);
            await manager.StopUdpAsync();
        }
    }

    private static async Task RunAllDemo(CommunicationManager manager)
    {
        Console.WriteLine("🚀 Running comprehensive communication demo...");
        
        await RunUdpDemo(manager);
        await Task.Delay(1000);
        await RunTcpDemo(manager);
        await Task.Delay(1000);
        await RunSerialDemo(manager);
        
        Console.WriteLine("✅ All demos completed!");
    }

    private static string GetPrintableAscii(byte[] data)
    {
        return new string(data.Where(b => b >= 32 && b <= 126).Select(b => (char)b).ToArray());
    }
}